package org.concept.box.grid;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import org.concept.box.util.RandomUtils;

public class BoxGrid {
	private int height;
	private int width;
	private int[][] grid;

	public static final int SPACE_VALUE = 0;

	BoxGrid(int height, int width) {
		if (height < 2)
			throw new IllegalArgumentException("height<1");
		if (width < 2)
			throw new IllegalArgumentException("width<1");
		this.height = height;
		this.width = width;
		grid = new int[height][width];
	}

	private int getZeroPos() {
		for (int i = 1; i <= height; i++) {
			for (int j = 1; j <= width; j++) {
				if (getValue(i, j) == SPACE_VALUE) {
					return (i - 1) * width + j;
				}
			}
		}
		throw new IllegalStateException("no zero!!");
	}

	public void setValue(int height, int width, int value) {
		grid[height - 1][width - 1] = value;
	}

	public int getValue(int height, int width) {
		return grid[height - 1][width - 1];
	}

	public void move(int p1, int p2) {
		int w1 = p1 % width == 0 ? width : p1 % width;
		int h1 = (int) Math.ceil(1.0 * p1 / width);
		int w2 = p2 % width == 0 ? width : p2 % width;
		int h2 = (int) Math.ceil(1.0 * p2 / width);
		int value1 = getValue(h1, w1);
		int value2 = getValue(h2, w2);
		setValue(h1, w1, value2);
		setValue(h2, w2, value1);
	}

	public boolean allowTurnLeft() {
		int pos = getZeroPos();
		int w = pos % width == 0 ? width : pos % width;
		if (w != 1) {
			return true;
		} else {
			return false;
		}
	}

	public BoxGrid turnLeft() {
		BoxGrid newGrid = clone();
		int pos = newGrid.getZeroPos();
		int w = pos % width == 0 ? width : pos % width;
		if (w != 1) {
			newGrid.move(pos, pos - 1);
			return newGrid;
		}
		throw new TurnException("can't turn left !!");
	}

	public boolean allowTurnRight() {
		int pos = getZeroPos();
		int w = pos % width == 0 ? width : pos % width;
		if (w != width) {
			return true;
		} else {
			return false;
		}
	}

	public BoxGrid turnRight() {
		BoxGrid newGrid = clone();
		int pos = newGrid.getZeroPos();
		int w = pos % width == 0 ? width : pos % width;
		if (w != width) {
			newGrid.move(pos, pos + 1);
			return newGrid;
		}
		throw new TurnException("can't  turn right!!");
	}

	public boolean allowTurnUp() {
		int pos = getZeroPos();
		int h = (int) Math.ceil(1.0 * pos / width);
		if (h != 1) {
			return true;
		} else {
			return false;
		}
	}

	public BoxGrid turnUp() {
		BoxGrid newGrid = clone();
		int pos = newGrid.getZeroPos();
		int h = (int) Math.ceil(1.0 * pos / width);
		if (h != 1) {
			newGrid.move(pos, pos - width);
			return newGrid;
		}
		throw new TurnException("can't turn up !!");
	}

	public boolean allowTurnDown() {
		int pos = getZeroPos();
		int h = (int) Math.ceil(1.0 * pos / width);
		if (h != height) {
			return true;
		} else {
			return false;
		}
	}

	public BoxGrid turnDown() {
		BoxGrid newGrid = clone();
		int pos = newGrid.getZeroPos();
		int h = (int) Math.ceil(1.0 * pos / width);
		if (h != height) {
			newGrid.move(pos, pos + width);
			return newGrid;
		}
		throw new TurnException("can't turn down !!");
	}

	public boolean isNotSorted() {
		return !isSorted();
	}

	public boolean isSorted() {
		int value = 1;
		for (int i = 1; i <= height; i++) {
			for (int j = 1; j <= width; j++) {
				if (i == height && j == width) {
					if (getValue(i, j) != SPACE_VALUE)
						return false;
				} else {
					if (getValue(i, j) != value++)
						return false;
				}
			}
		}
		return true;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + Arrays.hashCode(grid);
		result = prime * result + height;
		result = prime * result + width;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		final BoxGrid other = (BoxGrid) obj;
		if (!Arrays.equals(grid, other.grid))
			return false;
		if (height != other.height)
			return false;
		if (width != other.width)
			return false;
		return true;
	}

	@Override
	public String toString() {
		StringBuilder result = new StringBuilder((width + 2) * height);
		for (int i = 1; i <= height; i++) {
			for (int j = 1; j <= width; j++) {
				result.append(getValue(i, j));
			}
			result.append(System.getProperty("line.separator"));
		}
		return result.toString();
	}

	@Override
	public BoxGrid clone() {
		BoxGrid boxGrid = new BoxGrid(height, width);
		for (int i = 1; i <= height; i++) {
			for (int j = 1; j <= width; j++) {
				boxGrid.setValue(i, j, getValue(i, j));
			}
		}
		return boxGrid;
	}

	public static BoxGrid asSorted(int height) {
		return asSorted(height, height);
	}

	public static BoxGrid asSorted(int height, int width) {
		BoxGrid boxgrid = new BoxGrid(height, width);
		int value = 1;
		for (int i = 1; i <= height; i++) {
			for (int j = 1; j <= width; j++) {
				if (i == height && j == width) {
					boxgrid.setValue(i, j, SPACE_VALUE);
				} else {
					boxgrid.setValue(i, j, value++);
				}
			}
		}
		return boxgrid;
	}

	public static BoxGrid asRandom(int height) {
		return asRandom(height, height);
	}

	public static BoxGrid asRandom(int height, int width) {
		BoxGrid boxgrid = new BoxGrid(height, width);
		List<Integer> list = new LinkedList<Integer>();
		int max = height * width;
		for (int n = 0; n < max; n++) {
			list.add(n);
		}
		for (int i = 1; i <= height; i++) {
			for (int j = 1; j <= width; j++) {
				int p = RandomUtils.random(0, max--);
				boxgrid.setValue(i, j, list.remove(p));
			}
		}
		return boxgrid;
	}

	public static BoxGrid asString(String desc, String split) {
		String[] strs = desc.split(split);
		if (strs.length < 2 || strs[0].length() < 2)
			throw new IllegalArgumentException("illegal char!!");
		int height = strs.length;
		int width = strs[0].length();
		BoxGrid boxgrid = new BoxGrid(height, width);
		for (int i = 1; i <= height; i++) {
			for (int j = 1; j <= width; j++) {
				boxgrid.setValue(i, j, Integer.parseInt(String
						.valueOf(strs[i - 1].charAt(j - 1))));
			}
		}
		return boxgrid;
	}

	public static void main(String[] args) {
		BoxGrid g = asString("523a416a780", "a");
		System.out.println(g);
		System.out.println(g.allowTurnLeft());
		System.out.println(g.allowTurnRight());
		System.out.println(g.allowTurnUp());
		System.out.println(g.allowTurnDown());
	}
}
